package com.boarsoft.boar.message.server.listener;

import java.util.Map;

import javax.annotation.PostConstruct;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.boarsoft.common.Util;
import com.boarsoft.message.MessageConfig;
import com.boarsoft.message.bean.RecipientMQ;
import com.boarsoft.message.core.MessageContext;
import com.boarsoft.message.core.MessageService;
import com.boarsoft.rpc.bean.RpcReferenceConfig;
import com.boarsoft.rpc.bean.RpcRegistry;
import com.boarsoft.rpc.bean.RpcServiceConfig;
import com.boarsoft.rpc.core.RpcContext;
import com.boarsoft.rpc.core.RpcCore;
import com.boarsoft.rpc.listener.RpcListener;

/**
 * 监听RPC注册表变更，自动为消息接收者（MessageService的Provider）创建对应的收件人服务的引用<br>
 * 此方案容易导致消息接口方随意注册收件人队列又不删除导致消息服务崩溃
 * 
 * @author Mac_J
 *
 */
public class MsgSrvRpcListener implements RpcListener {
	private static final Logger log = LoggerFactory.getLogger(MsgSrvRpcListener.class);

	@Autowired
	protected RpcContext rpcContext;
	@Autowired
	protected MessageContext messageContext;

	@PostConstruct
	public void init() {
		// 添加到RpcCore的事件监听器列表，以便在有新的节点上线时自动注册其上消息服务的引用
		RpcCore.getCurrentInstance().addListener(this);
		// 初始化完成收件人配置，为每个收件人注册远程服务引用
		Map<String, RpcRegistry> rm = rpcContext.getRegistryMap();
		for (RpcRegistry r : rm.values()) {
			if (MessageConfig.getAddr().equals(r.getKey())) {
				continue;
			}
			this.onUpdateRegistry(r);
		}
	}

	@Override
	public void onRemoveLink(String addr, String reason) {
		// Nothing to do
	}

	@Override
	public void onRegister(RpcRegistry reg) {
		// Nothing to do
	}

	@Override
	public void onDeregister(RpcRegistry reg) {
		// Nothing to do
	}

	@Override
	public void onUpdateRegistry(RpcRegistry rr) {
		// 收到注册表推送时，需要从中找出收件人RPC服务（提供者）配置，以获取方法ID
		String remoteAddr = rr.getKey();
		RpcCore rpcCore = RpcCore.getCurrentInstance();
		String msc = MessageService.class.getName();
		// 来自远程的serviceMap
		Map<String, RpcServiceConfig> sm = rr.getServiceMap();
		// 配置注册表中的引用（由MsgSrvRpcListener初始化
		RpcRegistry myReg = rpcContext.getMyRegistry();
		Map<String, RpcReferenceConfig> myRefMap = myReg.getReferenceMap();
		//
		String svcId = null;
		for (String sk : sm.keySet()) {
			RpcServiceConfig sc = sm.get(sk);
			if (!msc.equals(sc.getInterfaceName())) {
				continue;
			}
			if ("message".equals(sc.getGroup()) && "server".equals(sc.getName())) {
				continue;
			}
			svcId = sc.getId();
			// 要支持自动注册，服务提供者必须要配置全局唯一的ID
			if (Util.strIsEmpty(svcId)) {
				log.warn("Message service has no global id: {}", sc);
				continue;
			}
			RecipientMQ rq = messageContext.getRecipientQueue(svcId);
			if (rq == null) {
				log.warn("Create message recipient {} for RPC service reference {}", sc, svcId);
				// continue; // 需要先配置为收件人
				rq = new RecipientMQ();
				rq.setCatalog(sc.getGroup());
				rq.setCode(svcId); // 用RpcService的ID作为队列的唯一编号
				rq.setName(sc.getName());
				rq.setVersion(sc.getVersion());
				rq.setDynamic(true);// 动态创建的收件人队列需要在无服务提供者是动态删除
				messageContext.addRecipientQueue(svcId, rq);
			}
			// 到本地注册表找是否已注册此服务的引用
			RpcReferenceConfig rc = myRefMap.get(svcId);
			if (rc == null) {
				log.info("Register RPC reference for recipient queue {}", rq);
				try {
					// RecipientMQ中的以下属性需要与RpcReferenceConfig中的属性对应
					rc = new RpcReferenceConfig(rq.getCatalog(), rq.getName(), //
							MessageService.class.getName(), rq.getVersion(), rq.getCode());
					rpcContext.registReference(rc, myReg);
				} catch (Exception e) {
					log.error("Error on regist reference for recipient queue {}", rq, e);
				}
			}
			log.info("Found message service provider {} -> {}", remoteAddr, sk);
			rpcCore.putProvider(rr, sc);
			rpcCore.registMethods(remoteAddr, sc, rc);
		}
	}

	public RpcContext getRpcContext() {
		return rpcContext;
	}

	public void setRpcContext(RpcContext rpcContext) {
		this.rpcContext = rpcContext;
	}

	public MessageContext getMessageContext() {
		return messageContext;
	}

	public void setMessageContext(MessageContext messageContext) {
		this.messageContext = messageContext;
	}
}